/*
 * TransiCast City Visions
 * (c) 2014 Joachim Pfeiffer
 * www.transicast.com
 */
package com.transicast.cityvisions;

import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.metaio.sdk.jni.Vector2d;

import android.graphics.Color;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class Arrival implements Comparable<Arrival> {
	private String										mBlock = "";
	private String										mRoute = "";
	private long										mEst = -1;
	private String										mDest = "";
	private String										mLocid = "";
	private double										mLat = 0.0d;
	private double										mLon = 0.0d;
	private long										mCoordinatesSet = 0L;
	private double										mSpeed = 5.0d; // m/s
	private double										mDistanceInterpolated = -999999.9999d;
	private long										mInterpolatedDistanceTimeSet = 0L;
	private TextView									mLabelView = null;
	private ARView										mOwner;
	private ExponentialSmoother							mDestinationLabelSmoother;

	public String getBlock() {
		return mBlock;
	}
	public String getRoute() {
		return mRoute;
	}
	public String getDest() {
		return mDest;
	}
	public double getLat() {
		return mLat;
	}
	public double getLon() {
		return mLon;
	}
	public String getLocid() {
		return mLocid;
	}
	public long getEst() {
		return mEst;
	}
	public double getSpeed() {
		return mSpeed;
	}
	public void setBlock(String block) {
		mBlock = block;
	}
	public void setRoute(String route) {
		mRoute = route;
	}
	public void setDest(String dest) {
		mDest = dest;
	}
	public void setCoordinates(String lat, String lon) {
		double newLat;
		double newLon;

		if (lat == null || lon == null)
			return;

		try {
			newLat = Double.parseDouble(lat);
		} catch (Exception e) { 
			return;
		}
		try {
			newLon = Double.parseDouble(lon);
		} catch (Exception e) { 
			return;
		}
		mCoordinatesSet = System.currentTimeMillis();

		// First set; do not change speed
		if (mLat == 0.0d && mLon == 0.0d || mLat == newLat && mLon == newLon) {
			mLat = newLat;
			mLon = newLon;
			return;
		}

		double distanceNewToOld = Utilities.distance(newLat, newLon, mLat, mLon) * 1000;
		mSpeed = Math.max(0.0d, distanceNewToOld / 30.0d); // Next update in 30s
		mSpeed = Math.min(mSpeed, 20.0f);
		mLat = newLat;
		mLon = newLon;
	}
	public long getCoordinatesSetTime() {
		return mCoordinatesSet;
	}
	public void updateBus(Vector2d screenCoords) {
		if (mLabelView != null && screenCoords != null) {
			mLabelView.setX(screenCoords.getX());
			mLabelView.setY(screenCoords.getY() + 20.0f);
			String[] cleanedupRouteDest = Utilities.collapseRouteAndDesc(mRoute, mDest);
			mLabelView.setText(" " + cleanedupRouteDest[0] + " " + cleanedupRouteDest[1] + " ");
		}
	}
	public void setVisible(boolean visible) {
		if (mLabelView == null)
			return;
		if (visible)
			mLabelView.setVisibility(View.VISIBLE);
		else
			mLabelView.setVisibility(View.INVISIBLE);
	}
	public void setLocid(String locid) {
		mLocid = locid;
	}
	public void setEst(String est) {
		if (est != null)
			try {
				mEst = Long.parseLong(est);
			} catch (Exception e) { }
	}
	public void setHeading(String heading) { }

	public double calculateDistanceToStopInterpolated() {
		if (mLat == 0.0d && mLon == 0.0d || mCoordinatesSet == 0L)
			return -999999.9999d;
		long now = System.currentTimeMillis();
		if (mDistanceInterpolated == -999999.9999d) {
			mDistanceInterpolated = Utilities.distance(mLat, mLon, Parcour.getStopLat(mLocid), Parcour.getStopLon(mLocid)) * 1000.0d;
			mInterpolatedDistanceTimeSet = now;
			return mDistanceInterpolated;
		}
		mDistanceInterpolated -= mSpeed * (now - mInterpolatedDistanceTimeSet) / 1000.0d;
		mInterpolatedDistanceTimeSet = now;
		return mDistanceInterpolated;
	}

	public float overlapRejection(Map<String, Arrival> arrivals, float threshold) {
		float result = 0.0f;
		for (Arrival arrival : arrivals.values()) {
			if (result != 0.0f)
				continue;
			if (mBlock.equals(arrival.getBlock()))
				continue;
			int myDirection = Parcour.getDirection(mLocid);
			int arrivalDirection = Parcour.getDirection(arrival.getLocid());
			if (myDirection != arrivalDirection)
				continue;
			double myDistance = calculateDistanceToStopInterpolated();
			double othersDistance = arrival.calculateDistanceToStopInterpolated();
			if (myDirection > 0)
				if (myDistance - threshold <= othersDistance && othersDistance < myDistance) {
					result = threshold - (float)(myDistance - othersDistance);
				}
			if (myDirection < 0)
				if (myDistance + threshold >= othersDistance && othersDistance > myDistance) {
					result = threshold - (float)(othersDistance - myDistance);
				}
			arrival.mDistanceInterpolated -= result * myDirection;
		}

		return result;
	}

	@Override
	public int compareTo(Arrival compare) {
		return compare.mDistanceInterpolated > mDistanceInterpolated ? 1 : mDistanceInterpolated > compare.mDistanceInterpolated ? -1 : 0;
	}

	public void clear() {
		if (mOwner != null && mLabelView != null) {
			final RelativeLayout layout = (RelativeLayout)mOwner.findViewById(R.id.aroverlay);
			mOwner.runOnUiThread(new Runnable() {
				public void run() { 
					layout.removeView(mLabelView);
				} 
			});
		}
	}
	
	@SuppressWarnings({"unchecked", "rawtypes"})
	public static Map<String, Arrival> sort(ConcurrentHashMap<String, Arrival> in) {
		List list = new LinkedList(in.entrySet());
		Collections.sort(list, new Comparator() {

			@Override
			public int compare(Object lhs, Object rhs) {
				return ((Comparable)((Map.Entry)(lhs)).getValue()).compareTo(((Map.Entry)(rhs)).getValue());
			}
		});
		
		Map<String, Arrival> out = new LinkedHashMap<String, Arrival>();
		for (Iterator i = list.iterator(); i.hasNext();) {
			Map.Entry entry = (Map.Entry)i.next();
			out.put((String)entry.getKey(), (Arrival)entry.getValue());
		}
		return out;
	}

	public Vector2d smooth(Vector2d coords) {
		return mDestinationLabelSmoother.pushValues(coords);
	}

	public Arrival(ARView owner, String block, String route, String est, String dest, String locid, String lat, String lon, String heading) {
		if (block != null)
			mBlock = block;
		if (route != null)
			mRoute = route;
		setEst(est);
		if (dest != null)
			mDest = dest;
		if (locid != null)
			mLocid = locid;
		setCoordinates(lat, lon);
		mLabelView = new TextView(owner);
		mLabelView.setVisibility(View.INVISIBLE);
		mLabelView.setTextColor(Color.RED);
		mLabelView.setBackgroundColor(Color.LTGRAY);
		mLabelView.setAlpha(0.9f);
		final RelativeLayout layout = (RelativeLayout)owner.findViewById(R.id.aroverlay);
		owner.runOnUiThread(new Runnable() {
			public void run() { 
				layout.addView(mLabelView);
			}
		});
		mOwner = owner;
		mDestinationLabelSmoother = new ExponentialSmoother(8.5f, 4);
	}
}
